/*
 * Mailer.cpp
 *
 *  Created on: Jun 24, 2009
 *      Author: ortal
 */

#include "Mailer.h"
#include <vector>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <utility>

/* Declaration of the wrapper function */
extern "C"
{
	void *wrapper_func2(void *args)
	{
		Mailer* curThread = (Mailer*)args;
		cout << "Enter scheduling algo number, then quanta value:" <<endl;
		cout << "RoundRobin (1), Priority (2):" <<endl;
		//cout << "testing- press Enter for RR with quantum 3"<<endl;
		int algoNum;
		int quanta;
		cin >> algoNum;
		cin >> quanta;
		curThread->run(algoNum, quanta);
	}
}

void Mailer::start(){

	// Initialize the thread itself
	if (pthread_create(&_pThread, NULL, wrapper_func2, this) != 0)
	{
		perror("OSThread thread creation failed");
		exit(1);
	}
}

Mailer::Mailer(int buffSize, int numOfNode){
	pthread_mutex_init(&_shellMutex,NULL);
	//*inBox = new xx(buffSize);
	cout << "Mailer Created" <<endl;
	_numOfNodes = numOfNode;
	_mailbox = new BoundMailBox(buffSize);
	pthread_cond_init(&_shellCondVar, NULL);
}

Mailer::~Mailer(){

}

void Mailer::Recv(/*Message* msg , Node* node*/)
{
	if(! _mailbox->isEmpty() )
	{
		pthread_mutex_lock(&_MailboxMutex);
		Message* currMsg = _mailbox->getNextMsg();
		pthread_mutex_unlock(&_MailboxMutex);
		pthread_cond_signal(&_MailboxCondVar);
		string type = currMsg->getMsgType();
		if (type.compare("rt") == 0)
		{
			int rcvr = currMsg->getReceiver();
			map<int,Node*>::iterator iter;
			iter = _nodes.find(rcvr);
			Node* n =iter->second;
			_scheduler->IncTT(n);  //update tt
			n->addLast(currMsg);

		}
		else // system msg
		{
			//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^0-0^^^^^^^^^^^^^^^^^^^^^^^^^
			string command = currMsg->getSysMsg()->getCommand();
			if (command.compare("killNode") == 0)
			{


				int id = currMsg->getSysMsg()->getUniqeId();
				map<int,Node*>::iterator iter;
				queue<Node*> q;
				for(iter = _nodes.begin(); iter != _nodes.end(); ++iter)
				{
					int currId = ((Node*)(iter->second))->getId();
					// non-active node
					if(id == currId)
					{
						((Node*)(iter->second))->stop();
					}
					else
					{
						((Node*)(iter->second))->getRT()->Reset();
						((Node*)(iter->second))->FirstSend();
						q.push(iter->second);
					}
				}
				_scheduler->UpdateNewReadyQ(q);


			} //End kill NOde
			if (command.compare("killAll") == 0)
			{
				map<int,Node*>::iterator iter;
				for(iter = _nodes.begin(); iter != _nodes.end(); ++iter)
				{
					((Node*)(iter->second))->stop();
				}
			}
			if (command.compare("reviveNode") == 0)
			{
				int nodeToRevive = currMsg->getSysMsg()->getUniqeId();
				map<int,Node*>::iterator iter;
				iter = _nodes.find(nodeToRevive);
				((Node*)(iter->second))->revive();
			}
			if (command.compare("printRT") == 0)
			{
				int nodeToRevive = currMsg->getSysMsg()->getUniqeId();
				map<int,Node*>::iterator iter;
				iter = _nodes.find(nodeToRevive);
				((Node*)(iter->second))->PrintRT();
			}
			if (command.compare("SendPacket") == 0)
			{

			}
		}
	}
	else
	{
//		cout<<"Mailbox: INBOX EMPTY"<< endl;
//		sleep(5);
//		printRTs();
	}
}

/*
 *
 * */
void Mailer::run(int algorithm, int quanta)
{
	bool stablized = false;
	bool alive = true;
	_scheduler = new Scheduler(algorithm,_nodesVec, quanta,this);
	//starting all the nodes.
	map<int,Node*> :: iterator iter;
	for(iter = _nodes.begin(); iter != _nodes.end(); ++iter )
	{
		cout<<"starting node "<< iter->first <<" from Mailer"<< endl;
		Node* node= iter->second;
		node->start();
	}

	while (alive)
	{	//TODO: handle messeges
		Recv();
		stablized = _scheduler->Schedule();
		if(stablized)
		{
//			pthread_cond_signal(&_shellCondVar);
//		/	sleep(3);
//			cout<<"_<_<_<_<_< MAILER-: stabilized - mutex lock"<<endl;
			pthread_mutex_lock(&_shellMutex);
			pthread_cond_wait(&_shellCondVar,&_shellMutex);
			pthread_mutex_unlock(&_shellMutex);
//			cout<<"_<_<_<_<_< MAILER-:- mutex UNlock"<<endl;
			_scheduler->PrintTT();
		}

	}
}

//TODO: synchronize - check buffer size, here or at Node.send
void Mailer::addFirst(Message* msg)
{
		_mailbox->addFirst(msg);
}
void Mailer::addLast(Message* msg)
{
		_mailbox->addLast(msg);
}


// =================================== Initialization===================================

/* Create a new node, and add it to nodes map
 * #1 element is node ID
 * #2+ elements are neighbors
 * called from 'for' loop
 */
void Mailer::createNode(vector<int> v){
	Node* node = new Node(v, this);
	_nodesVec.push_back(node);
	int nid = node->getId();
	pair <int,Node*> p1 (nid,node);
	_nodes.insert(p1);
	_nodeIds.insert(_nodeIds.end(),nid);

	//cout<<"_node size "<< _nodes.size() <<endl;
}

// goes through all nodes and creates RTs
void Mailer::createRTs(){
	map<int, Node*>::iterator it;
	for(it=_nodes.begin(); it != _nodes.end(); it++)
	{
		((Node*)(it->second))->InitRT(_nodeIds);
	}
}

bool Mailer::fullInbox()
{
	return _mailbox->isFull();
}

bool Mailer::emptyInbox()
{
	return _mailbox->isEmpty();
}

// =================================== Getters===================================

pthread_cond_t Mailer::getShellCondVar()
{
	return _shellCondVar;
}

pthread_mutex_t Mailer::getShellMutex()
{
	return _shellMutex;
}

pthread_cond_t Mailer::getMailboxCondVar()
{
	return _MailboxCondVar;
}

pthread_mutex_t Mailer::getMailboxMutex()
{
	return _MailboxMutex;
}

// =================================== auxilary===================================


void Mailer::printRTs(){
	cout << "=====printing RTs=========="<<endl;
	map<int, Node*>::iterator it;
	for(it  =_nodes.begin();it != _nodes.end(); it++)
	{
		((Node*)(it->second))->PrintRT();
	}
	cout << "_______________________"<<endl;
}

void Mailer::printGraph()
{
	cout<<"***** Printing the graph *****"<<endl;
	map<int,Node*> :: iterator iter;
	for(iter = _nodes.begin(); iter != _nodes.end(); ++iter )
	{
		 cout << "node's " << iter->first << " neighbors are: " << endl;
		 (iter->second)->printNeighbors();
	}
}
